﻿using System;
using SlimDX.Direct3D11;
using SlimDX;
using SlimDX.DXGI;
using Device = SlimDX.Direct3D11.Device;
using Resource = SlimDX.Direct3D11.Resource;
using Buffer = SlimDX.Direct3D11.Buffer;

namespace InsTron
{
    static class SimpleModelsCreator
    {
        public static SimpleModel CreatePlatformModel(Device device)
        {
            int vertexCount = 6 * Constants.PlatformSize * Constants.PlatformSize;
            int vertexSize = 32;
            float startPos = -Constants.PlatformSize * Constants.SegmentSize / 2f;

            var vertices = new DataStream(vertexSize * vertexCount, true, true);
            for (int i = 0; i < Constants.PlatformSize; i++) //x axis
            {
                for (int j = 0; j < Constants.PlatformSize; j++) //z axis
                {
                    // top triangle
                    vertices.Write(new Vector3(startPos + i * Constants.SegmentSize, 0f, startPos + j * Constants.SegmentSize));
                    vertices.Write(new Vector2(0f, 0f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                    vertices.Write(new Vector3(startPos + (i + 1) * Constants.SegmentSize, 0f, startPos + (j + 1) * Constants.SegmentSize));
                    vertices.Write(new Vector2(1f, 1f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                    vertices.Write(new Vector3(startPos + (i + 1) * Constants.SegmentSize, 0f, startPos + j * Constants.SegmentSize));
                    vertices.Write(new Vector2(1f, 0f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                    // bottom triangle
                    vertices.Write(new Vector3(startPos + i * Constants.SegmentSize, 0f, startPos + j * Constants.SegmentSize));
                    vertices.Write(new Vector2(0f, 0f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                    vertices.Write(new Vector3(startPos + i * Constants.SegmentSize, 0f, startPos + (j + 1) * Constants.SegmentSize));
                    vertices.Write(new Vector2(0f, 1f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                    vertices.Write(new Vector3(startPos + (i + 1) * Constants.SegmentSize, 0f, startPos + (j + 1) * Constants.SegmentSize));
                    vertices.Write(new Vector2(1f, 1f));
                    vertices.Write(new Vector3(0f, 1f, 0f));
                }
            }
            vertices.Position = 0;
            
            Buffer buffer = new Buffer(device, vertices, vertexSize * vertexCount, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            return new SimpleModel(buffer, vertexCount, vertexSize);
        }


        public static SimpleModel CreateHorizontalWallModel(Device device)
        {
            float shiftX = 0.5f;
            float shiftZ = 0;
            Vector3 normal = new Vector3(0.0f, 0.0f, -1.0f);

            return CreateWallModel(device, shiftX, shiftZ, ref normal);
        }


        public static SimpleModel CreateVerticalWallModel(Device device)
        {
            float shiftX = 0;
            float shiftZ = 0.5f;
            Vector3 normal = new Vector3(-1.0f, 0.0f, 0.0f);

            return CreateWallModel(device, shiftX, shiftZ, ref normal);
        }


        private static SimpleModel CreateWallModel(Device device, float shiftX, float shiftZ, ref Vector3 normal)
        {
            int vertexCount = 6;
            int vertexSize = 24;
            float height = 1.20f;

            var vertices = new DataStream(vertexSize * vertexCount, true, true);
            // top triangle            
            vertices.Write(new Vector3(-shiftX, height, -shiftZ));
            vertices.Write(normal);
            vertices.Write(new Vector3(shiftX, height, shiftZ));
            vertices.Write(normal);
            vertices.Write(new Vector3(shiftX, 0.0f, shiftZ));
            vertices.Write(normal);
            // bottom triangle
            vertices.Write(new Vector3(-shiftX, height, -shiftZ));
            vertices.Write(normal);
            vertices.Write(new Vector3(shiftX, 0.0f, shiftZ));
            vertices.Write(normal);
            vertices.Write(new Vector3(-shiftX, 0.0f, -shiftZ));
            vertices.Write(normal);

            vertices.Position = 0;

            Buffer buffer = new Buffer(device, vertices, vertexSize * vertexCount, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            return new SimpleModel(buffer, vertexCount, vertexSize);
        }


        public static SimpleModel CreateSkySphereModel(Device device)
        {
            int tessFactor = 20;
            int vertexCount = tessFactor * 2 * (tessFactor - 1) * 6;
            int vertexSize = 32;
            double radius = 400;

            var vertices = new DataStream(vertexSize * vertexCount, true, true);
            for (int i = 0; i < 2 * tessFactor; i++)
            {
                double hAngle1 = i * Math.PI / tessFactor;
                double hAngle2 = (i + 1) * Math.PI / tessFactor;

                for (int j = 0; j < tessFactor; j++)
                {
                    double vAngle1 = j * Math.PI / tessFactor;
                    double vAngle2 = (j + 1) * Math.PI / tessFactor;

                    float yTop = (float)(radius * Math.Cos(vAngle1));
                    float yBot = (float)(radius * Math.Cos(vAngle2));

                    double radiusTop = radius * Math.Sin(vAngle1);
                    double radiusBot = radius * Math.Sin(vAngle2);

                    double radiusTopRatio = 1;
                    double radiusBotRatio = 1;

                    float xTopLeft = (float)(radiusTop * Math.Sin(hAngle1));
                    float xBotLeft = (float)(radiusBot * Math.Sin(hAngle1));
                    float xTopRight = (float)(radiusTop * Math.Sin(hAngle2));
                    float xBotRight = (float)(radiusBot * Math.Sin(hAngle2));

                    float zTopLeft = (float)(radiusTop * -Math.Cos(hAngle1));
                    float zBotLeft = (float)(radiusBot * -Math.Cos(hAngle1));
                    float zTopRight = (float)(radiusTop * -Math.Cos(hAngle2));
                    float zBotRight = (float)(radiusBot * -Math.Cos(hAngle2));

                    if (j != 0)
                    {
                        vertices.Write(new Vector3(xTopLeft, yTop, zTopLeft));
                        vertices.Write(new Vector2((float)(radiusTopRatio * hAngle1 * 0.5 / Math.PI), (float)(vAngle1 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                        vertices.Write(new Vector3(xTopRight, yTop, zTopRight));
                        vertices.Write(new Vector2((float)(radiusTopRatio * hAngle2 * 0.5 / Math.PI), (float)(vAngle1 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                        vertices.Write(new Vector3(xBotRight, yBot, zBotRight));
                        vertices.Write(new Vector2((float)(radiusBotRatio * hAngle2 * 0.5 / Math.PI), (float)(vAngle2 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                    }
                    if (j != tessFactor - 1)
                    {
                        vertices.Write(new Vector3(xTopLeft, yTop, zTopLeft));
                        vertices.Write(new Vector2((float)(radiusTopRatio * hAngle1 * 0.5 / Math.PI), (float)(vAngle1 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                        vertices.Write(new Vector3(xBotRight, yBot, zBotRight));
                        vertices.Write(new Vector2((float)(radiusBotRatio * hAngle2 * 0.5 / Math.PI), (float)(vAngle2 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                        vertices.Write(new Vector3(xBotLeft, yBot, zBotLeft));
                        vertices.Write(new Vector2((float)(radiusBotRatio * hAngle1 * 0.5 / Math.PI), (float)(vAngle2 / Math.PI)));
                        vertices.Write(new Vector3(0.0f, 0.0f, 0.0f));
                    }
                }
            }
            vertices.Position = 0;

            Buffer buffer = new Buffer(device, vertices, vertexSize * vertexCount, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
            return new SimpleModel(buffer, vertexCount, vertexSize);
        }
    }
}
